﻿/*------------------------------------------------------------------------

CSMART_PTR.HPP v2.4

Copyright 楊志賢 CxxlMan, 2010 - 2014
All Rights Reserved

CSmart_Ptr 採用計數器技術，所以不能用於有循環參照的物件。

CSmart_Ptr 可放入任何種類的物件，包括 cxxlObject 物件，但已放入 Smart_Ptr、PSmart_Ptr
或 P2Smart_Ptr 的物件，不能再放入 CSmart_Ptr。而放入過 CSmart_Ptr 的物件也不能再放入
其他智彗指標，包括 CSmart_Ptr。

------------------------------------------------------------------------*/


#ifndef CSMART_PTR_HPP_CxxlMan
#define CSMART_PTR_HPP_CxxlMan

#include <stdio.h>
#include <CRITICAL.HPP>


namespace CxxlMan
{

template <typename T>
class CSmart_Ptr;

template <typename L,typename K>
CSmart_Ptr<L> cxxlFASTCALL CSmart_Cast(const CSmart_Ptr<K> &Src);


class CSmart_Base
{

protected:

  // 用於記錄有多少個 Bag 被建立
  class Bag_Rec
  {
    CriticalSec csObject;
    unsigned int Counter; // 記錄 Bag 被建立的計數，當減至 0 時，物件被解構

  public:

    // Constructor
    Bag_Rec()
    {
      Counter = 1;
    }

    void cxxlFASTCALL IncCounter()
    {
      csObject.Enter();
      ++Counter;
      csObject.Leave();
    }

    // 當減至 0 時回覆 true，物件應被解構
    bool cxxlFASTCALL DecCounter()
    {
      CriticalSecHandle AutoCSH(csObject);
      --Counter;

      if(Counter == 0)
        return true;
      else
        return false;
    }
  };

};

template <typename T>
class CSmart_Ptr:private CSmart_Base
{

  // 用於包裹一個物件的某一個介面
  class Bag
  {
    Bag_Rec *m_pBag_Rec;

    CriticalSec csObject;
    unsigned int Counter; // 記錄 Bag 被放入 CSmart_Ptr 的計數，當減至 0 時，此 Bag 被解構

    T *Obj;  // 包裹物件的某一個介面

  public:

    // Constructor，物件第一次被放入
    Bag(T *pObj_Arg)
    {
      m_pBag_Rec = new Bag_Rec;
      Counter = 1;
      Obj = pObj_Arg;
    }

    // Constructor，物件不同介面再次被放入
    Bag(T *pObj_Arg, Bag_Rec *pBag_Rec_Arg)
    {
      m_pBag_Rec = pBag_Rec_Arg;
      m_pBag_Rec->IncCounter();
      Counter = 1;
      Obj = pObj_Arg;
    }

    // Destructor
    ~Bag()
    {
      if(m_pBag_Rec->DecCounter())
      {
        delete m_pBag_Rec;
        delete Obj;
      }
    }

    // 取得包裹的物件
    T * cxxlFASTCALL Get_Obj() const
    {
      return Obj;
    }

    Bag_Rec * cxxlFASTCALL Get_Bag_Rec() const
    {
      return m_pBag_Rec;
    }

   void cxxlFASTCALL IncCounter()
    {
      csObject.Enter();
      ++Counter;
      csObject.Leave();
    }

    bool cxxlFASTCALL DecCounter()
    {
      CriticalSecHandle AutoCSH(csObject);
      --Counter;

      if(Counter == 0)
        return true;
      else
        return false;        
    }

  };


  Bag *m_pBag;

  // Constructor，物件不同介面再次被放入
  CSmart_Ptr(T *pObj_Arg, Bag_Rec *pBag_Rec_Arg)
  {
    if(pObj_Arg == NULL)
      m_pBag = NULL;
    else
      m_pBag = new Bag(pObj_Arg, pBag_Rec_Arg);
  }

public:

  // Constructor，物件第一次被放入
  CSmart_Ptr(T *pObj_Arg)
  {
    if(pObj_Arg == NULL)
      m_pBag = NULL;
    else
      m_pBag = new Bag(pObj_Arg);
  }

  // Copy Constructor
  CSmart_Ptr(const CSmart_Ptr<T> &Src)
  {
    m_pBag = Src.m_pBag;
    if(m_pBag != NULL)
      m_pBag->IncCounter();
  }

  // Destructor
  virtual ~CSmart_Ptr()
  {
    if(m_pBag != NULL)
      if(m_pBag->DecCounter())
        delete m_pBag;
  }

  // 拷備函數
  CSmart_Ptr<T> & cxxlFASTCALL operator=(const CSmart_Ptr<T> &Src)
  {
    if(&Src != this)
    {
      if(m_pBag != NULL)
        if(m_pBag->DecCounter())
          delete m_pBag;

      m_pBag = Src.m_pBag;
      if(m_pBag != NULL)
        m_pBag->IncCounter();
    }

    return *this;
  }

  T * cxxlFASTCALL operator->() const
  {
    return (m_pBag == NULL)? NULL:(m_pBag->Get_Obj());
  }

  // 用自動轉形方式取得包裹的物件
  cxxlFASTCALL operator T*() const
  {
    return (m_pBag == NULL)? NULL:(m_pBag->Get_Obj());
  }

  bool cxxlFASTCALL isNULL() const
  {
    return (m_pBag == NULL);
  }

  // 放棄持有的物件
  void cxxlFASTCALL Destroy()
  {
    if(m_pBag != NULL)
      if(m_pBag->DecCounter())
        delete m_pBag;

    m_pBag = NULL;
  }

  template <typename L,typename K>
  friend CSmart_Ptr<L> cxxlFASTCALL CSmart_Cast(const CSmart_Ptr<K> &Src);

};

/*
** 轉形函數
*/
template <typename L,typename K>
CSmart_Ptr<L> cxxlFASTCALL CSmart_Cast(const CSmart_Ptr<K> &Src)
{

  if(!Src.isNULL())
  {
    K *temp1 = Src;
    L *temp2 = dynamic_cast<L *>(temp1);

    return CSmart_Ptr<L>(temp2, Src.m_pBag->Get_Bag_Rec());
  }
  else
    return CSmart_Ptr<L>((L *)NULL);
}

}  /* namespace CxxlMan */
#endif // CSMART_PTR_HPP_CxxlMan
